/* tripl/arch/arch_templ.c 
 * 
 * This file is part of tripl. 
 * 
 * tripl is free software: you can redistribute it and/or modify 
 * it under the terms of the GNU General Public License as published by 
 * the Free Software Foundation, either version 3 of the License, or 
 * (at your option) any later version. 
 * 
 * tripl is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
 * GNU General Public License for more details. 
 * 
 * You should have received a copy of the GNU General Public License 
 * along with tripl. If not, see <https://www.gnu.org/licenses/>
 */ 




#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string.h>

#include <errno.h>

#include <klist.h>
#include <tripl/tripl.h>
#include <tripl/limits.h>
#include <tripl/arch.h>



char 	*psave;

__arch_templ static int new_var(char *p, struct arch *);
__arch_templ static int parse_func(const char *str, struct arch *arch_fn);

__arch_templ int arch_do_keyword(char *p, struct arch *arch_fn) {
	psave = p;

	switch (*p) {
	case '$':
		/* '$ asdf = 0;' 	--- asdf = 0
		 * '$ asdf 0;'		--- asdf = 0
		 * '$ asdf;'		--- asdf = 0
		 * */

		p++;
		_skip_spaces(p);

		new_var(p, arch_fn);

		break;

	case '!':
		if (!arch_fn->arch_stack_dump) 
			exit(generic_error("unsupported 'stack-dump' in current mode", psave, lno));

		arch_fn->arch_stack_dump();

		break;

	case '@':
		++p;
		_skip_spaces(p);

		if (parse_func(p, arch_fn)) 
			exit(1);
		
		break;

	default:
		exit(generic_error("unknown keyword", psave, lno));

		break;
	}

	return 0;
}


__arch_templ static int new_var(char *p, struct arch *arch_fn) {
	char *name;
	int type, out, attr;
	
	getword(p, &type, &out);

	if (type != WORDTYPE_NAME) 
		exit(generic_error("a name for variable required", psave, lno));

	name = strndup(p, out);

	p += out;
	_skip_spaces(p);

	getword(p, &type, &out);

	switch (type) {
	case WORDTYPE_KWORD:
		if (out != '=') 
			exit(generic_error("keyword but it's not a '='", psave, lno));
		
		++p;
		_skip_spaces(p);

		getword(p, &type, &out);

		if (type != WORDTYPE_NR) 
			exit(generic_error("a number required", psave, lno));

		_skip_word(p);

		break;

	case WORDTYPE_NR:
		/* to attribute (if have) */

		_skip_word(p);

		break;

	case 0:
		out = 0;

		break;

	default:
		exit(generic_error("bad word after variable name", psave, lno));
	}

	attr = getattrmask(p, vardef_attrtab);

	arch_fn->arch_new_var(name, out, attr);

	/* the name can't be free */

	return 0;
}

#define move_i()	while (isspace(str[i])) i++

__arch_templ static int parse_func(char *str, struct arch *arch_fn) {
	struct arg_entry 	*args, *n, *currarg;
	char 				*funcname, *name;
	int					type, out;

	size_t 				i, start, end;

	arg_init(&args);
	currarg = args;
	_skip_spaces(str);

	getword(str, &type, &out);

	if (type != WORDTYPE_NAME) 
		/* @, but not a name */
		exit(generic_error("a function name required", psave, lno));

	funcname = strndup(str, out);
	str += out;
	_skip_spaces(str);

	/* find the position of the first '(' */

	getword(str, &type, &out);

	if (type != WORDTYPE_KWORD || out != '(') 
		exit(generic_error("cannot find the left bracket", psave, lno));

	str++;
	_skip_spaces(str);


	while (getword(str, &type, &out)) {
		switch (type) {
		case WORDTYPE_NAME:
			currarg->arg_n = strndup(str, out);
			str += out;

			break;

		case WORDTYPE_NR:
			currarg->arg_v = out;
			skip_word(str);

			break;

		case WORDTYPE_STR:
			currarg->arg_s = strndup(str + 1, out - 2);
			str += out;
		
			break;
		}
		

		n = malloc(sizeof(struct klist_node));

		klist_init(n);
		klist_add(currarg, n);

		currarg = n;

		_skip_spaces(str);
	}

	arch_fn->arch_funcall(funcname, args);

	/* free */

	currarg = args;

	for (;;) {
		if (currarg->arg_s) 
			free(currarg->arg_s);

		if (currarg->arg_n) 
			free(currarg->arg_n);

		free(currarg);

		currarg = currarg->next;
		
		if (currarg == args) 
			break;
	}

	return 0;
}
